/**
 * 1.当前时间
 * 2.日志等级
 * 3.线程ID
 * 4.文件名
 * 5.行号
 * 6.有效载荷，即消息主体
 * 7.日志器名称
 */

#ifndef __FORMATTER_HPP__
#define __FORMATTER_HPP__

#include "Message.hpp"
#include <memory>
#include <vector>
#include <cassert>
#include <sstream>

namespace MyLog
{
    class FormatterItem
    {
    public:
        using shptr = std::shared_ptr<FormatterItem> ;
        virtual void formatter(std::ostream &out,const LogMessage &message) = 0;
    };

    class TimeFormatter:public FormatterItem
    {
    public:
        TimeFormatter(const std::string& format="%Y-%m-%d %H:%M:%S")
            :_format(format)
        {
            if(format.empty())_format="%Y-%m-%d %H:%M:%S";
        }
        void formatter(std::ostream &out, const LogMessage &message) override
        {
            time_t time =message._currenttime;
            struct tm getTime;
            localtime_r(&time,&getTime);

            char buffer[128];
            strftime(buffer,sizeof(buffer)-1,_format.c_str(),&getTime);
            out<<buffer;
        }
    private:
        std::string _format;
    };

    class LogLevelFormatter:public FormatterItem
    {
    public:
        void formatter(std::ostream &out, const LogMessage &message)override
        {
            out<<LogLevel::toString(message._level);
        }
    };

    class ThreadIDFormatter:public FormatterItem
    {
    public:
        void formatter(std::ostream &out, const LogMessage &message)override
        {
            out<<message._tid;
        }
    };

    class FileNameFormatter:public FormatterItem
    {
    public:
        void formatter(std::ostream &out, const LogMessage &message)override
        {
            out<<message._filename;
        }
    };

    class LineFormatter:public FormatterItem
    {
    public:
        void formatter(std::ostream &out, const LogMessage &message)override
        {
            out<<message._line;
        }
    };

    class PayLoadFormatter:public FormatterItem
    {
    public:
        void formatter(std::ostream &out, const LogMessage &message)override
        {
            out<<message._payload;
        }
    };

    class LoggerFormatter:public FormatterItem
    {
    public:
        void formatter(std::ostream &out, const LogMessage &message)override
        {
            out<<message._loggername;
        }
    };

    class TableFormatter:public FormatterItem
    {
    public:
        void formatter(std::ostream &out, const LogMessage &message)override
        {
            out<<"\t";
        }
    };

    class NewLineFormatter:public FormatterItem
    {
    public:
        void formatter(std::ostream &out, const LogMessage &message)override
        {
            out<<"\n";
        }
    };

    class OtherFormatter:public FormatterItem
    {
    public:
        OtherFormatter(const std::string& str1="")
        :_str(str1)
        {}
        void formatter(std::ostream &out, const LogMessage &message)override
        {
            //这里应该输出_str，而不是message._loggername
            out<<_str;
        }
    private:
        std::string _str;
    };

    /**
     * %d 日期
     * %t 线程ID 
     * %p 日志级别
     * %c 日志器名称
     * %f 文件名
     * %l 行号
     * %m 消息
     * %n 换行
     * %T 缩进
     */
    #define PATTERN "[%d{%Y-%m-%d %H:%M:%S}][%t][%p][%c][%f:%l] %m%n"
    class Formatter
    {
    public:
        using ptr = std::shared_ptr<Formatter>;
        Formatter(const std::string& pattern=PATTERN)
            :_pattern(pattern)
        {
            assert(ParsePattern());
        }

        const std::string getPattern(){return _pattern;}
        std::string format(const LogMessage& message)
        {
            // 将_item里的元素逐个写入stringstream对象中，返回ss
            std::stringstream ss;
            for(auto &items :_item)
            {
                items->formatter(ss,message);
            }
            return ss.str();
        }

        std::ostream& format(std::ostream& out,const LogMessage& message)
        {
            // 将_item里的元素格式化输入到ostream流里
            // 重载输出流为了满足日志的输出位置的需求
            for(auto &items : _item)
            {
                items->formatter(out,message);
            }
            return out;
        }

        FormatterItem::shptr createItem(const std::string& key,const std::string& value)
        {
            if(key=="d")return FormatterItem::shptr(new TimeFormatter(value));
            if(key=="t")return FormatterItem::shptr(new ThreadIDFormatter());
            if(key=="p")return FormatterItem::shptr(new LogLevelFormatter());
            if(key=="c")return FormatterItem::shptr(new LoggerFormatter());
            if(key=="f")return FormatterItem::shptr(new FileNameFormatter());
            if(key=="l")return FormatterItem::shptr(new LineFormatter());
            if(key=="m")return FormatterItem::shptr(new PayLoadFormatter());
            if(key=="n")return FormatterItem::shptr(new NewLineFormatter());
            if(key=="T")return FormatterItem::shptr(new TableFormatter());
            if(key=="") return FormatterItem::shptr(new OtherFormatter(value));
            PrintError("没有对应的格式化字符");
            std::abort();
        }

        /**
         * 可能的输入格式
         * %aa%%cde[%d{%H:%M:%S}][%p]%T%m%n;
         */
    private:
        bool ParsePattern()
        {
            // 1.处理原始字符串--判断是否是%，不是就是原始字符
            std::vector<std::pair<std::string,std::string>> format;
            std::size_t pos = 0;
            std::size_t len = _pattern.size();
            std::string key, value;
            while (pos < len)
            {
                //先找到第一个%字符
                while(pos < len && _pattern[pos] != '%')
                {
                    value.push_back(_pattern[pos]);
                    pos++;
                }
                //到这里可能找到%号了，如果没找到就说明传入的格式不对
                if(pos == len)
                {
                    PrintError("没有指定要格式化的字符，请正确输入参数");
                    return false;
                }

                //处理%%d，因为%需要一个%需要两个%%才能正常显示
                if(pos + 1 < len && _pattern[pos + 1] == '%')
                {
                    value.push_back('%');
                    pos += 2;
                    continue;
                }

                //判空是为了防止字符串开头出现%aa这种情况，没有符合格式化的字符，，就放进去了一个空的pair
                if(!value.empty())
                {
                    format.push_back({"",value});
                    value.clear();
                }

                //程序走到这里说明pos位置是%，让pos自增，走到格式化字符的位置
                pos += 1;
                //可能pos加一后等于len了，这个时候也是参数没传对，输出日期的格式化字符是大写的，但是
                //处理日期的格式化字符的逻辑不在这里，所以可以用判断是否小写字母

                if (pos < len && isalpha(_pattern[pos]))
                {
                    key = _pattern[pos];//保存格式字符串
                }
                else
                {
                    PrintError(std::to_string(pos)+"位置后缺少格式化字符，请正确传参");
                    return false;
                }
                
                //格式化字符放进去后需要一定pos
                pos+=1;
                //处理日期的子字符串
                if (pos < len && _pattern[pos] == '{')
                {
                    //[%d{%H:%M:%S}]
                    pos += 1;
                    while (pos < len && _pattern[pos] !='}')
                    {
                        value.push_back(_pattern[pos++]);
                    }

                    //如果是因为到达边界结束的循环，说明没有}，参数没传对
                    if (pos == len)
                    {
                        PrintError(std::to_string(pos)+"位置缺失}，请正确传参");
                        return false;
                    }

                    //执行到这里说明找到了}，继续处理后面的字符串
                    pos += 1;
                }
                //[%d{%H:%M:%S}]
                //%d后面跟着{}，所以d应该作为{}的key值
                format.push_back({key,value});
                key.clear();
                value.clear();
            }
            // 2.根据解析得到的数据初始化格式化子项数组成员
            for(auto &it : format)
            {
                _item.push_back(createItem(it.first,it.second));
            }
            return true;
        }
    
        std::string _pattern;
        std::vector<FormatterItem::shptr> _item;
    };
}

#endif